package org.rency.crawler.fetch.state.impl;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Resource;
import org.rency.crawler.common.CrawlerConstants;
import org.rency.crawler.common.exception.MachineStateException;
import org.rency.crawler.domain.state.FetchStateDomain;
import org.rency.crawler.domain.state.FetchStateKind;
import org.rency.crawler.domain.state.FetchStateRuleDomain;
import org.rency.crawler.domain.state.FetchStatus;
import org.rency.crawler.fetch.handler.FetchHandler;
import org.rency.crawler.fetch.repository.FetchStateRepository;
import org.rency.crawler.fetch.repository.FetchStateRuleRepository;
import org.rency.crawler.fetch.state.FetchStateCarrier;
import org.rency.crawler.fetch.state.FetchStateFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;

/**
 * 描述: ${TODO}<br>
 * 最近修改时间:2017/11/23 11:22<br>
 *
 * @author CaiyuRen
 * @version V1.0
 * @since 2017/11/23 11:22
 */
@Component
public class FetchStateFactoryImpl implements FetchStateFactory, BeanFactoryAware,CrawlerConstants {

  private static final Logger logger = LoggerFactory.getLogger(FetchStateFactoryImpl.class);

  private static final ConcurrentHashMap<String, FetchStateCarrier> CACHE = new ConcurrentHashMap<String, FetchStateCarrier>();

  @Resource
  private FetchStateRepository         fetchStateRepository;

  @Resource
  private FetchStateRuleRepository     fetchStateRuleRepository;

  private BeanFactory                  beanFactory;

  private Lock                         lock = new ReentrantLock();

  @Override
  public FetchStateCarrier getStateStar() {
    FetchStateDomain state = fetchStateRepository.find(FetchStateKind.STAR.getCode());
    if(state == null){
      String msg = String.format("未找到状态机:%s",FetchStateKind.STAR.getCode());
      logger.error(msg);
      throw new MachineStateException(msg);
    }
    FetchStateCarrier carrier = new FetchStateCarrier();
    BeanUtils.copyProperties(state,carrier);
    carrier.setFsId(state.getId());
    carrier.setFsService(this.getHandler(state.getFsService()));
    carrier.setFsStatus(FetchStatus.SUCCESS);
    return carrier;
  }

  @Override
  public FetchStateCarrier getNextState(String fsCode, FetchStatus status) {
    String cacheKey = this.getCacheKey(fsCode,status);
    FetchStateCarrier carrier = CACHE.getOrDefault(cacheKey,null);
    if(carrier == null){
      if(lock.tryLock()) {
        try {
          carrier = CACHE.getOrDefault(cacheKey,null);
          if (carrier == null) {
            carrier = this.loadNext(fsCode, status);
            if (carrier != null) {
              CACHE.putIfAbsent(cacheKey, carrier);
              carrier = CACHE.get(cacheKey);
            }
          }
        } finally {
          lock.unlock();
        }
      }
    }
    return carrier;
  }

  /**
   * 加载下一状态机
   * @param fsCode
   * @param status
   * @return
   */
  private FetchStateCarrier loadNext(String fsCode, FetchStatus status){
    logger.debug("加载状态机:{},{}",fsCode,status.getCode());
    //加载当前状态
    FetchStateDomain state = fetchStateRepository.find(fsCode);
    if(state == null){
      String msg = String.format("未找到状态机:%s",fsCode);
      logger.error(msg);
      throw new MachineStateException(msg);
    }

    //判断是否为终态
    if(FetchStateKind.END == FetchStateKind.get(fsCode)){
      return null;
    }

    //获取状态机关系
    FetchStateRuleDomain rule = fetchStateRuleRepository.find(fsCode,status.getCode());
    if(rule == null){
      String msg = String.format("未找到状态机规则:%s,%s",state.getFsName(),status.getMsg());
      logger.error(msg);
      throw new MachineStateException(msg);
    }

    //加载下一状态
    FetchStateDomain nextState = fetchStateRepository.find(rule.getNextFsCode());
    if(nextState == null){
      String msg = String.format("未找到状态机:%s",rule.getNextFsCode());
      logger.error(msg);
      throw new MachineStateException(msg);
    }
    FetchStateCarrier carrier = new FetchStateCarrier();
    BeanUtils.copyProperties(nextState,carrier);
    carrier.setFsId(nextState.getId());
    carrier.setFsService(this.getHandler(nextState.getFsService()));
    return carrier;
  }

  private String getCacheKey(String fsCode, FetchStatus status){
    return fsCode+COLON+status.getCode();
  }

  private FetchHandler getHandler(String handlerName){
    FetchHandler handler = beanFactory.getBean(handlerName, FetchHandler.class);
    if(handler == null){
      throw new RuntimeException("未找到["+handlerName+"]对象实例!");
    }
    return handler;
  }

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
  }
}
